<?php

namespace App\Extensions\Upload;

use Symfony\Component\HttpFoundation\File\UploadedFile;

class UploadFile
{
    /**
     * 应用实例
     *
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected $app;

    /**
     * 上传文件存储目录
     *
     * @var string
     */
    protected $directory = 'resources/uploads';

    /**
     * 允许上传的上传文件媒体类型
     *
     * @var array
     */
    protected $mimes = ['*'];

    /**
     * 允许上传的上传文件后缀
     *
     * @var array
     */
    protected $exts = ['*'];

    /**
     * 允许上传的大小，默认不限制
     *
     * @var int
     */
    protected $size = 0;

    /**
     * 错误信息
     *
     * @var string
     */
    protected $error;

    /**
     * 文件保存名称
     * @var string
     */
    protected $filename;
    /**
     * 目录时间戳
     *
     * @var boolean
     */
    protected $timestamp = true;

    /**
     * 创建一个文件上传服务实例
     *
     * @param $app
     */
    public function __construct($app)
    {
        $this->app = $app;
    }

    /**
     * 上传文件配置
     *
     * @param array $configs
     * @return void
     */
    public function configure($configs = [])
    {
        if (isset($configs['directory']) && !empty($configs['directory'])) {
            $this->directory = $this->directory . '/' . $configs['directory'];
        }

        if (isset($configs['filename']) && !empty($configs['filename'])) {
            $this->filename = $configs['filename'];
        }

        if (isset($configs['timestamp'])) {
            $this->timestamp = $configs['timestamp'];
        }

        $this->mimes = empty($configs['mimes']) ? ['*'] : $configs['mimes'];

        $this->exts = empty($configs['exts']) ? ['*'] : $configs['exts'];

        // 上传文件默认 10M
        $this->size = $configs['size'] ?? 10485760;
    }

    /**
     * 上传文件
     *
     * @param \Symfony\Component\HttpFoundation\File\UploadedFile
     * @return array | null
     */
    public function handle(UploadedFile $file = null)
    {
        $this->error = '';

        try {
            if (empty($file)) {
                throw new FileUploadException('没有可上传文件');
            }

            $json = $this->getJsonBody($file);

            if (!$this->isValidMime($json['mime'])) {
                throw new FileUploadException('上传文件媒体类型只能是：' . implode('、', $this->mimes));
            }

            if (!$this->isValidExt($json['ext'])) {
                throw new FileUploadException('上传文件后缀只能是：' . implode('、', $this->exts));
            }

            if (!$this->isValidSize($json['size'])) {
                throw new FileUploadException('上传文件长度不超过' . size2mb($this->size));
            }

            $json['savepath'] = $this->getSavepath();

            // 文件重命名
            $json['savename'] = $this->makeFilename($file);

            // 文件完成存储目录
//            $json['savepath'] = 'resources/uploads'.$json['savepath'];
            $fullpath = public_path() . "{$json['savepath']}";
            // 移动上传文件到指定的目录并重命名
            $file->move($fullpath, $json['savename']);

            return $json;

        } catch (FileUploadException $e) {
            $this->error = $e->getMessage();
        }

        return null;
    }

    /**
     * 获取上传文件错误信息
     *
     * @return string
     */
    public function getErrorMessage()
    {
        return $this->error ?? '由于未知的错误，文件上传失败';
    }

    /**
     * 存储目录地址
     *
     * @return string
     */
    protected function getSavepath()
    {
        $dir = $this->directory ? "{$this->directory}/" : "default";

        $dir = $this->app['config']['upload.uploads'] . "/$dir";
        if ($this->timestamp) {
            $dir = $dir . date('Y').'/'.date('m').'/'.date('d');
        }

        return $dir;
    }

    /**
     * 生成文件名称
     *
     * @param \Symfony\Component\HttpFoundation\File\UploadedFile
     * @return string
     */
    protected function makeFilename(UploadedFile $file)
    {
        if (empty($this->filename)) {
            return md5_file($file->getRealPath()) . ".{$file->getClientOriginalExtension()}";
        } else {
            return $this->filename;
        }
    }

    /**
     * 获取上传文件 json 格式返回信息
     *
     * @param \Symfony\Component\HttpFoundation\File\UploadedFile
     * @return array
     */
    protected function getJsonBody(UploadedFile $file)
    {
        return [
            'originalName' => $file->getClientOriginalName(),
            'mime' => $file->getClientMimeType(),
            'size' => $file->getClientSize(),
            'ext' => $file->getClientOriginalExtension()
        ];
    }

    /**
     * 验证上传文件媒体类型
     *
     * @param string $mime
     * @return boolean
     */
    protected function isValidMime(string $mime)
    {
        return in_array($mime, $this->mimes) || $this->mimes == ['*'];
    }

    /**
     * 验证上传文件后缀格式
     *
     * string $ext
     * @return bollean
     */
    protected function isValidExt(string $ext)
    {
        return in_array($ext, $this->exts) || $this->exts == ['*'];
    }

    /**
     * 验证上传文件大小
     *
     * @param int $size
     * @return boolean
     */
    protected function isValidSize(int $size)
    {
        return $this->size >= $size || $this->size === 0;
    }
}